﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;

namespace Homework4
{
    public static class DataParser
    {
        private const string FacultyFile = "Faculty.txt";
        private const string OfficeFile  = "Office.txt";
        private const string PhoneFile   = "Phone.txt";
        private const string CourseFile  = "Course.txt";

        public static void ParseFiles(string folderPath)
        {
            var dir = new DirectoryInfo(folderPath);
            if (!dir.Exists)
            {
                return;
            }

            var txtFiles = dir.GetFiles("*.txt");
            var facultyFile = txtFiles.FirstOrDefault(f => f.Name.Equals(FacultyFile));
            var officeFile  = txtFiles.FirstOrDefault(f => f.Name.Equals(OfficeFile));
            var phoneFile   = txtFiles.FirstOrDefault(f => f.Name.Equals(PhoneFile));
            var courseFile  = txtFiles.FirstOrDefault(f => f.Name.Equals(CourseFile));

            if (facultyFile == null || officeFile == null || phoneFile == null || courseFile == null)
            {
                return;
            }

            var instructors = new List<Instructor>();

            #region Parse Faculty.txt

            using (var sr = facultyFile.OpenText())
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();
                    var split = line.Split('\t');

                    var instructor = new Instructor();

                    #region Name (0)

                    var nameSplit = split[0].Split(' ');
                    instructor.FirstName = nameSplit[0];
                    instructor.LastName = nameSplit.Last();

                    for (var i = 1; i < nameSplit.Length - 1; i++)
                    {
                        instructor.MiddleName += nameSplit[i] + " ";
                    }

                    if (instructor.MiddleName != null)
                    {
                        instructor.MiddleName = instructor.MiddleName.Trim();
                    }

                    #endregion

                    #region Title (1)

                    if (split[1].Contains("and"))
                    {
                        var andIndex = split[1].IndexOf("and");
                        instructor.Titles.Add(split[1].Substring(0, andIndex).Trim());
                        instructor.Titles.Add(split[1].Substring(andIndex + 4).Trim());
                    }
                    else
                    {
                        instructor.Titles.Add(split[1]);
                    }

                    #endregion

                    #region Degree (2 & 3)

                    var degree = new Degree {Year = int.Parse(split[3])};
                    var spaceIndex = split[2].IndexOf(' ');
                    degree.Type = split[2].Substring(0, spaceIndex).Trim();
                    degree.School = split[2].Substring(spaceIndex + 1).Trim();

                    instructor.Degree = degree;

                    #endregion

                    #region Interests (4)

                    if (split.Length > 4)
                    {
                        var interests = split[4].Split(',');
                        foreach (var interest in interests)
                        {
                            instructor.Interests.Add(interest.Trim());
                        }
                    }

                    #endregion

                    instructors.Add(instructor);
                }
            }

            #endregion

            #region Parse Office.txt

            using (var sr = officeFile.OpenText())
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();
                    var split = line.Split('\t');

                    #region Name (0)

                    var nameSplit = split[0].Split(' ');
                    var lastName = nameSplit.Last();

                    var instructor = instructors.FirstOrDefault(i => i.LastName.Equals(lastName, StringComparison.InvariantCultureIgnoreCase));

                    if (instructor == null)
                    {
                        instructor = new Instructor { FirstName = nameSplit[0], LastName = lastName };

                        for (var i = 1; i < nameSplit.Length - 1; i++)
                        {
                            instructor.MiddleName += nameSplit[i] + " ";
                        }

                        if (instructor.MiddleName != null)
                        {
                            instructor.MiddleName = instructor.MiddleName.Trim();
                        }

                        instructors.Add(instructor);
                    }

                    #endregion

                    #region Office (1)

                    var office = new Office();

                    var location = split[1].Trim('(', ')');
                    office.Location = location;

                    #endregion

                    #region Hours (2+)

                    for (var i = 2; i < split.Length; i++)
                    {
                        office.Hours.Add(split[i]);
                    }

                    #endregion

                    instructor.Office = office;
                }
            }

            #endregion

            #region Parse Phone.txt

            using (var sr = phoneFile.OpenText())
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();
                    var split = line.Split('\t');

                    #region Name (0)

                    var nameSplit = split[0].Split(',');
                    var lastName = nameSplit[0];
                    var firstName = nameSplit[1].Trim();

                    var instructor = instructors.FirstOrDefault(i => i.LastName.Equals(lastName, StringComparison.InvariantCultureIgnoreCase));

                    if (instructor == null)
                    {
                        instructor = new Instructor { FirstName = firstName, LastName = lastName };
                        instructors.Add(instructor);
                    }

                    #endregion

                    #region Location (1)

                    if (instructor.Office.Location == null)
                    {
                        instructor.Office = new Office { Location = split[1] };
                    }

                    #endregion

                    #region Phone (2)

                    instructor.Office.Phone = split[2];

                    #endregion

                    #region Email (3)

                    instructor.Email = split[3];

                    #endregion
                }
            }

            #endregion

            #region Parse Course.txt

            using (var sr = courseFile.OpenText())
            {
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine();
                    var split = line.Split('\t');

                    #region Instructor Name (last...)

                    var lastItem = split.Last().Trim();
                    if (!lastItem.StartsWith("Instructor", StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;
                    }

                    var lastName = lastItem.Substring(15).Trim();

                    var instructor = instructors.FirstOrDefault(i => i.LastName.Equals(lastName, StringComparison.InvariantCultureIgnoreCase));

                    if (instructor == null)
                    {
                        continue;
                    }

                    #endregion

                    var course = new Course();

                    #region Id (0)

                    course.Id = int.Parse(split[0]);

                    #endregion

                    #region Name (1)

                    course.Name = split[1];

                    #endregion

                    #region Credits (2)

                    course.Credits = int.Parse(split[2].Trim('(', ')'));

                    #endregion

                    #region Description (3)

                    course.Description = split[3];

                    #endregion

                    #region Prereqs (4 or non-existent)

                    if (split.Length <= 4 || !split[4].StartsWith("Prerequisite", StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;
                    }

                    var prereqs = split[4].Split(',');

                    foreach (var prereq in prereqs)
                    {
                        course.Prerequisites.Add(prereq.StartsWith("Prerequisite", StringComparison.InvariantCultureIgnoreCase)
                                                     ? prereq.Substring(14).Trim()
                                                     : prereq.Trim());
                    }

                    #endregion

                    instructor.Courses.Add(course);
                }
            }

            #endregion

            #region Write Data to XML File

            using (var output = File.Create(Path.Combine(folderPath, "Instructors.xml")))
            {
                using (var writer = XmlWriter.Create(output, new XmlWriterSettings { Indent = true}))
                {
                    writer.WriteStartDocument();
                    writer.WriteStartElement("instructors");

                    foreach (var instructor in instructors)
                    {
                        instructor.WriteXml(writer);
                    }

                    writer.WriteEndElement();
                    writer.WriteEndDocument();
                }
            }

            #endregion

            #region Add Data to Database Schema

            var schema = DB.Schema;

            foreach (var instructor in instructors)
            {
                var instructorRow = schema.Instructor.AddInstructorRow("instructor",
                                                                       instructor.FirstName,
                                                                       instructor.MiddleName,
                                                                       instructor.LastName,
                                                                       instructor.Degree.Type,
                                                                       instructor.Degree.School,
                                                                       instructor.Degree.Year,
                                                                       instructor.Email,
                                                                       instructor.Office.Location,
                                                                       instructor.Office.Phone);

                foreach (var title in instructor.Titles)
                {
                    schema.Title.AddTitleRow("title", "instructor", instructorRow, title);
                }

                foreach (var hour in instructor.Office.Hours)
                {
                    schema.Hour.AddHourRow("hour", "instructor", instructorRow, hour);
                }

                foreach (var interest in instructor.Interests)
                {
                    schema.Interest.AddInterestRow("interest", "instructor", instructorRow, interest);
                }

                foreach (var course in instructor.Courses)
                {
                    var courseRow = schema.Course.AddCourseRow("course",
                                                               "instructor",
                                                               instructorRow,
                                                               course.Id,
                                                               course.Name,
                                                               course.Description,
                                                               course.Credits);

                    foreach (var prereq in course.Prerequisites)
                    {
                        schema.Prerequisite.AddPrerequisiteRow("prerequisite", "course", courseRow, prereq);
                    }
                }
            }

            DB.SyncSchemaToDatabase();

            #endregion
        }
    }

    public class Instructor
    {
        public string FirstName;
        public string MiddleName;
        public string LastName;
        public List<string> Titles = new List<string>();
        public Degree Degree = new Degree();
        public List<string> Interests = new List<string>();
        public string Email;

        public Office Office = new Office();
        public List<Course> Courses = new List<Course>();

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("instructor");

            writer.WriteElementString("first", FirstName);
            writer.WriteElementString("middle", MiddleName);
            writer.WriteElementString("last", LastName);

            writer.WriteStartElement("titles");
            foreach (var title in Titles)
            {
                writer.WriteElementString("title", title);
            }
            writer.WriteEndElement();

            Degree.WriteXml(writer);

            writer.WriteStartElement("interests");
            foreach (var interest in Interests)
            {
                writer.WriteElementString("interest", interest);
            }
            writer.WriteEndElement();

            writer.WriteElementString("email", Email);

            Office.WriteXml(writer);

            writer.WriteStartElement("courses");
            foreach (var course in Courses)
            {
                course.WriteXml(writer);
            }
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }

    public class Degree
    {
        public string Type;
        public string School;
        public int Year;

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("degree");

            writer.WriteElementString("type", Type);
            writer.WriteElementString("school", School);
            writer.WriteElementString("year", Year.ToString());

            writer.WriteEndElement();
        }
    }

    public class Office
    {
        public string Location;
        public List<string> Hours = new List<string>();
        public string Phone;

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("office");

            writer.WriteElementString("location", Location);

            writer.WriteStartElement("hours");
            foreach (var hour in Hours)
            {
                writer.WriteElementString("hour", hour);
            }
            writer.WriteEndElement();

            writer.WriteElementString("phone", Phone);

            writer.WriteEndElement();
        }
    }

    public class Course
    {
        public int Id;
        public int Credits;
        public string Name;
        public string Description;
        public List<string> Prerequisites = new List<string>();

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("course");

            writer.WriteAttributeString("id", Id.ToString());
            writer.WriteAttributeString("credits", Credits.ToString());

            writer.WriteElementString("name", Name);
            writer.WriteElementString("description", Description);

            writer.WriteStartElement("prerequisites");
            foreach (var prerequisite in Prerequisites)
            {
                writer.WriteElementString("prerequisite", prerequisite);
            }
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}
